use base64::Engine;
use base64::engine::general_purpose;
use crypto::buffer::{BufferResult, ReadBuffer, WriteBuffer};
use crypto::{aes, blockmodes, buffer, symmetriccipher};
use encoding_rs::GBK;
use rpassword::read_password;
use serde::{Deserialize, Serialize};
use std::io::{BufRead, BufReader, Read, Write};
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::thread::{sleep, spawn};
use std::{fs, io, time};
use totp_rs::{Algorithm, Secret, TOTP};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let c = load_config()?;
    conn(&c)
}

// 加载配置信息
fn load_config() ->  Result<Config, Box<dyn std::error::Error>> {
    let entries = fs::read_dir(".")?;
    let mut file= String::new();
    for entry in entries {
        if let Ok(entry) = entry {
            let file_name = entry.file_name().to_string_lossy().to_string();
            if file_name.to_lowercase().ends_with(".ovpn") {
                if file.is_empty() {
                    file = file_name;
                }else{
                    return Err(Box::from("目录下找到多个.ovpn文件"));
                }
            }
        }
    }
    if file.is_empty() {
        return Err(Box::from("目录下未找到.ovpn文件"));
    }

    let content = fs::read_to_string(".config");
    match content {
        Ok(content) => {
            print!("密码：");
            io::stdout().flush().unwrap(); // 刷新标准输出缓冲区
            let password = String::from(read_password().unwrap().trim());
            let mut c = Config::from_string(&content,&password);
            c.password = password;
            c.file = file;
            Ok(c)
        },
        Err(_) => {
            print!("用户名: ");
            let mut username = String::new();
            io::stdout().flush().unwrap(); // 刷新标准输出缓冲区
            io::stdin().read_line(&mut username).unwrap();
            username = String::from(username.trim());

            print!("密码：");
            io::stdout().flush().unwrap(); // 刷新标准输出缓冲区
            let password = String::from(read_password().unwrap().trim());

            print!("密钥：");
            io::stdout().flush().unwrap(); // 刷新标准输出缓冲区
            let secret = String::from(read_password().unwrap().trim());
            let c = Config { username, secret , password, file};
            fs::write(".config", c.to_string()).expect("Failed to write config file");
            Ok(c)
        }
    }
}

// 开始连接
fn conn(c: &Config) -> Result<(), Box<dyn std::error::Error>> {
    let mut child = Command::new("openvpn")
        .arg("--config")
        .arg(c.file.clone())
        .arg("--auth-user-pass")
        .stdout(Stdio::piped())
        .stdin(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()?;

    let mut stdin = child.stdin.take().unwrap();
    let stdout = child.stdout.take().unwrap();
    let stderr = child.stderr.take().unwrap();

    // 创建通信通道
    let (tx, rx) = mpsc::channel();

    // stderr 监控线程
    spawn(move || {
        let mut reader = BufReader::new(stderr);
        let mut str = String::new();
        loop {
            let mut bytes: [u8; 1024] = [0; 1024];
            match reader.read(&mut bytes[..]) {
                Ok(len) => {
                    if len == 0 {
                        continue;
                    }
                    let msg = gbk_to_utf8(&mut bytes[0..len]);
                    println!("{}", msg);
                    str += &msg;
                    if str.contains("Enter Auth Username") {
                        tx.send("username").unwrap();
                        str.clear();
                    }
                    if str.contains("Enter Auth Password") {
                        tx.send("password").unwrap();
                        str.clear();
                    }
                }
                Err(e) => {
                    println!("Error reading from stdout: {}", e);
                }
            }
            sleep(time::Duration::from_millis(100));
        }
    });

    // stdout 监控线程
    spawn(move || {
        let reader = BufReader::new(stdout);
        for line in reader.split(b'\n') {
            match line {
                Ok(line) => {
                    println!("info:{}", gbk_to_utf8(&*line))
                }
                Err(e) => {
                    println!("Error reading from stdout: {}", e);
                }
            }
        }
    });

    // 主线程处理 stdin 响应
    loop {
        if let Ok(action) = rx.recv_timeout(time::Duration::from_secs(10)) {
            println!("action:{}", action);
            match action {
                "username" => {
                    stdin
                        .write_all(utf8_to_gbk(c.username.as_str()).as_ref())
                        .unwrap();
                    stdin.flush().unwrap();
                }
                "password" => {
                    let code = generate_code(c.secret.to_string());
                    let password = c.password.to_owned() + code.as_str();
                    stdin
                        .write_all(utf8_to_gbk(password.as_str()).as_ref())
                        .unwrap();
                    stdin.flush().unwrap();
                }
                _ => {}
            }
        }
    }
}

fn gbk_to_utf8(bytes: &[u8]) -> String {
    let (cow, _, had_errors) = GBK.decode(bytes);

    if had_errors {
        eprintln!("警告: GBK 解码时遇到错误");
    }

    cow.into_owned()
}

fn utf8_to_gbk(text: &str) -> Vec<u8> {
    let (cow, _, had_errors) = GBK.encode(text);

    if had_errors {
        eprintln!("警告: UTF-8 到 GBK 编码时遇到错误");
    }

    cow.into_owned()
}

fn generate_code(secret: String) -> String {
    let key = base32::decode(
        base32::Alphabet::Rfc4648 { padding: secret.ends_with("=") },
        secret.as_str(),
    )
    .expect("Failed to decode secret");
    let totp = TOTP::new(
        Algorithm::SHA1,
        6,
        1,
        30,
        Secret::Raw(key).to_bytes().unwrap(),
    )
    .unwrap();
    totp.generate_current().unwrap()
}

const AES_CBC_KEY: [u8; 32] = [
    58, 46, 32, 136, 95, 37, 234, 147, 159, 25, 177, 66, 175, 233, 49, 76, 47, 66, 146, 221, 55,
    123, 232, 184, 22, 195, 33, 201, 44, 64, 75, 96,
];

#[derive(Serialize, Deserialize)]
struct Config {
    pub secret: String,
    pub username: String,
    #[serde(skip)]
    pub password: String,
    #[serde(skip)]
    pub file: String,
}

impl Config {
    fn to_string(&self) -> String {
        let mut c = Config {
            secret: self.secret.clone(),
            username: self.username.clone(),
            password: String::new(),
            file: String::new(),
        };
        
        let vi = [AES_CBC_KEY.as_slice(), self.password.clone().as_bytes()].concat();
        let vi = md5::compute(vi);

        let ciphertext =
            aes256_cbc_encrypt(self.username.as_bytes(), &AES_CBC_KEY, vi.as_slice()).unwrap();
        let b64 = general_purpose::STANDARD.encode(ciphertext).into_bytes();
        c.username = String::from(String::from_utf8_lossy(b64.as_slice()));

        let ciphertext =
            aes256_cbc_encrypt(self.secret.as_bytes(), &AES_CBC_KEY, vi.as_slice()).unwrap();
        let b64 = general_purpose::STANDARD.encode(ciphertext).into_bytes();
        c.secret = String::from(String::from_utf8_lossy(b64.as_slice()));
        
        serde_json::to_string(&c).unwrap()
    }

    fn from_string(s: &String, password: &String) -> Config {
        let mut c: Config = serde_json::from_str(s.as_str()).unwrap();
        
        let vi = [AES_CBC_KEY.as_slice(), password.as_bytes()].concat();
        let vi = md5::compute(vi);
        
        let ciphertext = general_purpose::STANDARD.decode(c.username.clone()).unwrap();
        let plaintext =
            aes256_cbc_decrypt(ciphertext.as_slice(), &AES_CBC_KEY, vi.as_slice()).unwrap();
        c.username = String::from(String::from_utf8_lossy(plaintext.as_slice()));
        
        let ciphertext = general_purpose::STANDARD.decode(c.secret.clone()).unwrap();
        let plaintext =
            aes256_cbc_decrypt(ciphertext.as_slice(), &AES_CBC_KEY, vi.as_slice()).unwrap();
        c.secret = String::from(String::from_utf8_lossy(plaintext.as_slice()));
        
        c
    }
}

pub fn aes256_cbc_encrypt(
    data: &[u8],
    key: &[u8],
    iv: &[u8],
) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
    let mut encryptor =
        aes::cbc_encryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);

    let mut final_result = Vec::<u8>::new();
    let mut read_buffer = buffer::RefReadBuffer::new(data);
    let mut buffer = [0; 4096];
    let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);

    loop {
        let result = encryptor.encrypt(&mut read_buffer, &mut write_buffer, true)?;

        final_result.extend(
            write_buffer
                .take_read_buffer()
                .take_remaining()
                .iter()
                .map(|&i| i),
        );

        match result {
            BufferResult::BufferUnderflow => break,
            BufferResult::BufferOverflow => {}
        }
    }

    Ok(final_result)
}

pub fn aes256_cbc_decrypt(
    encrypted_data: &[u8],
    key: &[u8],
    iv: &[u8],
) -> Result<Vec<u8>, symmetriccipher::SymmetricCipherError> {
    let mut decryptor =
        aes::cbc_decryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);

    let mut final_result = Vec::<u8>::new();
    let mut read_buffer = buffer::RefReadBuffer::new(encrypted_data);
    let mut buffer = [0; 4096];
    let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);

    loop {
        let result = decryptor.decrypt(&mut read_buffer, &mut write_buffer, true)?;
        final_result.extend(
            write_buffer
                .take_read_buffer()
                .take_remaining()
                .iter()
                .map(|&i| i),
        );
        match result {
            BufferResult::BufferUnderflow => break,
            BufferResult::BufferOverflow => {}
        }
    }

    Ok(final_result)
}
